function LandscapeVertex() {
	this.n = { x:0, y:0, z: 0 };
	this.t = { u:0, v:0 };
}

function clamp(v, min, max) {
	v = v < min ? min : v;
	return v > max ? max : v;
}


function load_landscape_material() {
        var fragmentShader = getShader(gl, "landscape2-fs");
        var vertexShader = getShader(gl, "landscape2-vs");

	var mat = new Object(); 

        s = gl.createProgram();
        gl.attachShader(s, vertexShader);
        gl.attachShader(s, fragmentShader);
        gl.linkProgram(s);

        if (!gl.getProgramParameter(s, gl.LINK_STATUS)) {
            alert("Could not initialise landscape shaders");
        }

        gl.useProgram(s);

	var attrib;
        mat.attribs = {};
	mat.attribs["pos"] = gl.getAttribLocation(s, "pos");
	mat.attribs["norm"] = gl.getAttribLocation(s, "norm");
	
        mat.uniforms = {}; 
	mat.uniforms["P"] = gl.getUniformLocation(s, "P");
        mat.uniforms["V"] = gl.getUniformLocation(s, "V");
        mat.uniforms["M"] = gl.getUniformLocation(s, "M");
        mat.uniforms["heightmap_scale"] = gl.getUniformLocation(s, "heightmap_scale");

	gl.useProgram(null);


	mat.s = s;

	return mat;
}

function generate_heightfield(size) {

	var hf = new Object();

	var xz_scale = 1.0;
	var y_scale = 3.5;

	hf.xz_scale = xz_scale;
	hf.y_scale = y_scale;
	hf.size = size;

	var terrain_resolution = size;
   	buf = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT*size*size);
	hmap = new Float32Array(buf);
	var heightmap_step = 10.0/(terrain_resolution - 1.0);
	hf.heightmap_scale = heightmap_step;  
	var p = new vec2(0,0);
	for(var y=0; y< size; ++y) {
                for(var x=0; x< size; ++x) {
			
			var p = new vec2(heightmap_step*x, heightmap_step*y);
			//var refval = fbm(new vec2(1.345, 0.847), 4, 2.0, 0.5);	
			var n = fbm(p, 4, 2.0, 0.5);
                        hmap[y*size + x] = n;//  0.1*Math.sin(x) + 0.1*Math.cos(y); // clamp(255.0*n, 0.0, 255.0);
                }
        }

	var w = terrain_resolution; 
	var h = terrain_resolution;

	hf.prim_type = gl.TRIANGLES;
	hf.vertex_stride = 6*Float32Array.BYTES_PER_ELEMENT;
	
 
	hf.ib_buf = ArrayBuffer(Uint16Array.BYTES_PER_ELEMENT*(size-1)*(size-1)*6);
	hf.ib = new Uint16Array(hf.ib_buf);
	for(var y=0; y< size-1; ++y) {
                for(var x=0; x< size-1; ++x) {
                        var cell = y*(size-1)+ x;
                        hf.ib[6*cell + 0] = y*w + x;
                        hf.ib[6*cell + 1] = w*(y+1) + x;
                        hf.ib[6*cell + 2] = w*(y+1) + x + 1;

                        hf.ib[6*cell + 3] = y*w + x;
                        hf.ib[6*cell + 4] = w*(y+1) + x + 1;
                        hf.ib[6*cell + 5] = y*w + x + 1;
                }
        }

	// create gl index buffer
	hf.ib_id = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, hf.ib_id);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, hf.ib, gl.STATIC_DRAW);
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

	var stride = 6; // in floats
	hf.vb_buf = ArrayBuffer(Float32Array.BYTES_PER_ELEMENT*stride*(size)*(size)); 
	hf.vb = new Float32Array(hf.vb_buf);
	var offset = new vec3(terrain_resolution*0.5, 0.0, terrain_resolution*0.5);
	for(var z=0; z< size; ++z) {
                for(var x=0; x< size; ++x) {
                        var cell = z*size+ x;
			
                        hf.vb[stride*cell + 0] = xz_scale*(x - offset.x);
			hf.vb[stride*cell + 1] = y_scale*(hmap[cell]);//  4*(hmap[cell]/255.0) - offset.y; 
			hf.vb[stride*cell + 2] = xz_scale*(z - offset.z);
			hf.vb[stride*cell + 3] = 0.0;
			hf.vb[stride*cell + 4] = 0.0;
			hf.vb[stride*cell + 5] = 0.0;
                }
        }
	
	// calc normals
	var n = new vec3();
	for(var y=1; y< terrain_resolution-1; ++y) {
        	for(var x=1; x< terrain_resolution-1; ++x) {
                        var cell = y*terrain_resolution + x;

                        var height_dx = hf.vb[stride*(cell + 1) + 1] - hf.vb[stride*(cell - 1) + 1];
                        var height_dz = hf.vb[stride*(cell + size)+1] - hf.vb[stride*(cell - size)+1];
			n.x = height_dx; 	
			n.z = height_dz; 	
			n.y = 2*heightmap_step; 	
			normalize2(n, n);
                        hf.vb[6*cell + 3] = n.x;
                        hf.vb[6*cell + 4] = n.y;
                        hf.vb[6*cell + 5] = n.z;
                }
        }

	// create gl vertex buffer
	hf.vb_id = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, hf.vb_id);
        gl.bufferData(gl.ARRAY_BUFFER, hf.vb, gl.STATIC_DRAW);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);

	return hf;
}

function draw_landscape(m, mat, model, view, proj) {

	gl.useProgram(mat.s);
	gl.bindBuffer(gl.ARRAY_BUFFER, m.vb_id);	
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ib_id);	
	
	if(mat.attribs.pos!=-1) {	
        	gl.enableVertexAttribArray(mat.attribs.pos);
        	gl.vertexAttribPointer(mat.attribs.pos, 3, gl.FLOAT, false, m.vertex_stride, 0);
	}
	if(mat.attribs.norm!=-1) {	
        	gl.enableVertexAttribArray(mat.attribs.norm);
        	gl.vertexAttribPointer(mat.attribs.norm, 3, gl.FLOAT, false, m.vertex_stride, 4*3);
	}

	//gl.cullFace(gl.FRONT_AND_BACK);

	if(mat.uniforms.M)
       		gl.uniformMatrix4fv(mat.uniforms.M, false, model);
	if(mat.uniforms.V)
       		gl.uniformMatrix4fv(mat.uniforms.V, false, view);
	if(mat.uniforms.P)
       		gl.uniformMatrix4fv(mat.uniforms.P, false, proj);

	if(mat.uniforms.heightmap_scale)
       		gl.uniform1f(mat.uniforms.heightmap_scale, m.heightmap_scale);

	//gl.drawElements(m.prim_type, m.ib.length/2, gl.UNSIGNED_SHORT, 0);
	gl.drawElements(m.prim_type, m.ib.length, gl.UNSIGNED_SHORT, 0);

	gl.useProgram(null);
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
